package io.openaev.rest.vulnerability;

import io.openaev.aop.LogExecutionTime;
import io.openaev.aop.RBAC;
import io.openaev.database.model.Action;
import io.openaev.database.model.ResourceType;
import io.openaev.rest.helper.RestBehavior;
import io.openaev.rest.vulnerability.form.*;
import io.openaev.rest.vulnerability.service.VulnerabilityService;
import io.openaev.utils.mapper.VulnerabilityMapper;
import io.openaev.utils.pagination.SearchPaginationInput;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.transaction.Transactional;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
@Tag(
    name = "Vulnerability API",
    description = "Operations related to Vulnerabilities (CVE and more)")
public class VulnerabilityApi extends RestBehavior {

  public static final String VULNERABILITY_API = "/api/vulnerabilities";

  private final VulnerabilityService vulnerabilityService;
  private final VulnerabilityMapper vulnerabilityMapper;

  @LogExecutionTime
  @Operation(summary = "Search Vulnerabilities")
  @PostMapping(VULNERABILITY_API + "/search")
  @RBAC(actionPerformed = Action.SEARCH, resourceType = ResourceType.VULNERABILITY)
  public Page<VulnerabilitySimple> searchVulnerabilities(
      @Valid @RequestBody SearchPaginationInput input) {
    return vulnerabilityService
        .searchVulnerabilities(input)
        .map(vulnerabilityMapper::toVulnerabilitySimple);
  }

  @Operation(
      summary = "Get a Vulnerability by ID",
      description = "Fetches detailed Vulnerability info by ID")
  @GetMapping(VULNERABILITY_API + "/{vulnerabilityId}")
  @RBAC(
      resourceId = "#vulnerabilityId",
      actionPerformed = Action.READ,
      resourceType = ResourceType.VULNERABILITY)
  public VulnerabilityOutput getVulnerability(@PathVariable String vulnerabilityId) {
    return vulnerabilityMapper.toVulnerabilityOutput(
        vulnerabilityService.findById(vulnerabilityId));
  }

  @Operation(
      summary = "Get a Vulnerability by external ID",
      description = "Fetches detailed Vulnerability info by external Vulnerability ID")
  @GetMapping(VULNERABILITY_API + "/external-id/{externalId}")
  @RBAC(
      resourceId = "#externalId",
      actionPerformed = Action.READ,
      resourceType = ResourceType.VULNERABILITY)
  public VulnerabilityOutput getVulnerabilityByExternalId(@PathVariable String externalId) {
    return vulnerabilityMapper.toVulnerabilityOutput(
        vulnerabilityService.findByExternalId(externalId));
  }

  @Operation(summary = "Create a new Vulnerability")
  @PostMapping(VULNERABILITY_API)
  @RBAC(actionPerformed = Action.CREATE, resourceType = ResourceType.VULNERABILITY)
  @Transactional(rollbackOn = Exception.class)
  public VulnerabilitySimple createVulnerability(
      @Valid @RequestBody VulnerabilityCreateInput input) {
    return vulnerabilityMapper.toVulnerabilitySimple(
        vulnerabilityService.createVulnerability(input));
  }

  @Operation(summary = "Bulk insert Vulnerabilities")
  @LogExecutionTime
  @PostMapping(VULNERABILITY_API + "/bulk")
  @RBAC(actionPerformed = Action.CREATE, resourceType = ResourceType.VULNERABILITY)
  public void bulkInsertVulnerabilitiesForCollector(
      @Valid @RequestBody @NotNull VulnerabilityBulkInsertInput input) {
    this.vulnerabilityService.bulkUpsertVulnerabilities(input);
  }

  @Operation(summary = "Update an existing Vulnerability")
  @PutMapping(VULNERABILITY_API + "/{vulnerabilityId}")
  @RBAC(
      resourceId = "#vulnerabilityId",
      actionPerformed = Action.WRITE,
      resourceType = ResourceType.VULNERABILITY)
  @Transactional(rollbackOn = Exception.class)
  public VulnerabilitySimple updateVulnerability(
      @PathVariable String vulnerabilityId, @Valid @RequestBody VulnerabilityUpdateInput input) {
    return vulnerabilityMapper.toVulnerabilitySimple(
        vulnerabilityService.updateVulnerability(vulnerabilityId, input));
  }

  @Operation(summary = "Delete a Vulnerability")
  @DeleteMapping(VULNERABILITY_API + "/{vulnerabilityId}")
  @RBAC(
      resourceId = "#vulnerabilityId",
      actionPerformed = Action.DELETE,
      resourceType = ResourceType.VULNERABILITY)
  @Transactional(rollbackOn = Exception.class)
  public void deleteVulnerability(@PathVariable String vulnerabilityId) {
    vulnerabilityService.deleteById(vulnerabilityId);
  }
}
